/**
 * Created by tolya-yanot
 * Creation date:  10.05.13 19:29
 */
package {
import com.adobe.utils.AGALMiniAssembler;

import flash.display.BitmapData;
import flash.display3D.Context3D;
import flash.display3D.Context3DBlendFactor;
import flash.display3D.Context3DProgramType;
import flash.display3D.Context3DTextureFormat;
import flash.display3D.Context3DVertexBufferFormat;
import flash.display3D.IndexBuffer3D;
import flash.display3D.Program3D;
import flash.display3D.VertexBuffer3D;
import flash.display3D.textures.Texture;
import flash.geom.Matrix3D;
import flash.geom.Point;
import flash.utils.getTimer;

public class Water {

    [Embed(source="/mask.jpg")]
    private const maskClass:Class;
    private const maskBitmapData:BitmapData = new maskClass().bitmapData;
    private var maskTexture:Texture;

    [Embed(source="/water.jpg")]
    private const waterClass:Class;
    private const waterBitmapData:BitmapData = new waterClass().bitmapData;
    private var waterTexture:Texture;

    private var shaderProgram:Program3D;

    private var maskVertexBuffer:VertexBuffer3D;
    private var maskVertexVector:Vector.<Number>;

    private var waterVertexBuffer:VertexBuffer3D;
    private var waterVertexVector:Vector.<Number>;

    private var indexBuffer:IndexBuffer3D;
    private var indexVector:Vector.<uint>;

    public function init(context3D:Context3D, swfWidth:int, swfHeight:int, width:int, height:int):void {
        // mask texture

        var t:BitmapData = new BitmapData(2048, 2048, false, 0);
        t.copyPixels(maskBitmapData, maskBitmapData.rect, new Point());
        maskTexture = context3D.createTexture(t.width, t.height, Context3DTextureFormat.BGRA, false);
        maskTexture.uploadFromBitmapData(t, 0);
        t.dispose();

        // water texture

        t = new BitmapData(256, 256, false, 0);
        t.copyPixels(waterBitmapData, waterBitmapData.rect, new Point());
        waterTexture = context3D.createTexture(t.width, t.height, Context3DTextureFormat.BGRA, false);
        waterTexture.uploadFromBitmapData(t, 0);
        t.dispose();

        // mask vertex
        var w:Number = width * 2 / swfWidth;
        var h:Number = height * 2 / swfHeight;
        var u:Number = maskBitmapData.width / 2048;
        var v:Number = maskBitmapData.height / 2048;

        maskVertexVector = new <Number>[
            // x, y, z, u, v
            -w / 2, h / 2, 0, 0, 0,
            -w / 2, -h / 2, 0, 0, v,
            w / 2, -h / 2, 0, u, v,
            w / 2, h / 2, 0, u, 0
        ];

        maskVertexBuffer = context3D.createVertexBuffer(maskVertexVector.length / 5, 5);
        maskVertexBuffer.uploadFromVector(maskVertexVector, 0, maskVertexVector.length / 5);

        // water vertex
        u = width / waterBitmapData.width;
        v = height / waterBitmapData.height;

        waterVertexVector = new <Number>[
            // u, v
            0, 0,
            0, v,
            u, v,
            u, 0
        ];

        waterVertexBuffer = context3D.createVertexBuffer(waterVertexVector.length / 2, 2);
        waterVertexBuffer.uploadFromVector(waterVertexVector, 0, waterVertexVector.length / 2);

        // index

        indexVector = new <uint>[0, 1, 2, 2, 3, 0];
        indexBuffer = context3D.createIndexBuffer(indexVector.length);
        indexBuffer.uploadFromVector(indexVector, 0, indexVector.length);

        // vertex shader

        const vertexShaderAssembler:AGALMiniAssembler = new AGALMiniAssembler();
        const vertexShaderSource:String = "m44 op, va0, vc0\n" + // output position
                "mov v0, va1\n" +  // copy mask uv
                "mov v1, va2\n";  // copy water uv
        vertexShaderAssembler.assemble(Context3DProgramType.VERTEX, vertexShaderSource);

        // fragment shader

        const fragmentShaderAssembler:AGALMiniAssembler = new AGALMiniAssembler();
        const fragmentShaderSource:String = "tex ft0, v0, fs0 <2d,linear,nomip>;\n" + // get mask texture color
                "mov ft0.w, ft0.x\n" +   // mask texture: alpha = red
                "add ft2, v1, fc0\n" + // water uv + water offset
                "tex ft1, ft2, fs1 <2d,linear,repeat,nomip>;\n" +   // get water texture color
                "mul ft0, ft0, ft1\n" + // multiply mask & water texture colors
                "mov oc, ft0"; // output color
        fragmentShaderAssembler.assemble(Context3DProgramType.FRAGMENT, fragmentShaderSource);

        // program

        shaderProgram = context3D.createProgram();
        shaderProgram.upload(vertexShaderAssembler.agalcode, fragmentShaderAssembler.agalcode);
    }

    public const position:Matrix3D = new Matrix3D();

    public function render(context3D:Context3D):void {
        context3D.setVertexBufferAt(0, maskVertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_3); // mask xyz
        context3D.setVertexBufferAt(1, maskVertexBuffer, 3, Context3DVertexBufferFormat.FLOAT_2); // mask uv
        context3D.setVertexBufferAt(2, waterVertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_2); // water uv
        context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, position, true); // position
        context3D.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, new <Number>[getTimer() / 10000, getTimer() / 100000, 0, 0]); // water texture offset
        context3D.setTextureAt(0, maskTexture);
        context3D.setTextureAt(1, waterTexture);
        context3D.setProgram(shaderProgram);
        context3D.setBlendFactors(Context3DBlendFactor.ONE, Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA);
        context3D.drawTriangles(indexBuffer);
    }
}
}
